// z80compiler.cpp : Defines the entry point for the console application.
//

#include <stdarg.h>  // For va_start, etc.
#include <regex>
#include <string>
#include <fstream>
#include <functional>

using namespace std;

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define BITFLAG(n) (1 << n)
#define	F BITFLAG(0)
#define	A BITFLAG(1)
#define AF (A | F)
#define C BITFLAG(2)
#define B BITFLAG(3)
#define BC (B | C)
#define E BITFLAG(4)
#define D BITFLAG(5)
#define DE (D | E)
#define L BITFLAG(6)
#define H BITFLAG(7)
#define HL (H | L)
#define IY BITFLAG(8)
#define IX BITFLAG(9)
#define R BITFLAG(10)
#define SP BITFLAG(11)
#define CARRY BITFLAG(12)
#define ZERO BITFLAG(13)
#define SIGN BITFLAG(14)
#define HFLAG BITFLAG(15)
#define OVERFLOWFLAG BITFLAG(16)
#define NFLAG BITFLAG(17)
#define FLAGS (CARRY|ZERO|SIGN|HFLAG|OVERFLOWFLAG|NFLAG)


class C6502Translator
{
public:
	C6502Translator( const string& oZ80Code )
		: m_oZ80Code( oZ80Code )
	{
		Replace(m_oZ80Code, "\r", "");
	};
	~C6502Translator() {};

	string Translate();
private:
	void Replace(string& str, const string& from, const string& to);
	bool GetNextLine(string& oLine);
	bool ParsingOK( const string& oSource, const char *szPattern, const char* szArgument1, const char* szArgument2 );
	bool ParsingOK( const string& oSource, const char *szPattern, const char* szArgument1 );
	bool FixArg( char* szArgument );
	int CountParameters( string pattern );
	string string_format(const string fmt, ...);
	void MakeParameterALabel( char *szArgument );
	void AddReferencedLabel( string oLabel );
	void AddDefinedLabel( string oLabel );
	string AddReferencedNotDefinedLabels();

	string m_oZ80Code;

	vector<string> m_oReferencedLabels;
	vector<string> m_oDefinedLabels;
};

void C6502Translator::AddReferencedLabel( string oLabel )
{
	if ( !oLabel.empty() )
	{
		if ( oLabel.substr(0,1) == "(" && oLabel.substr(oLabel.size()-1, 1) == ")" )
		{
			oLabel = oLabel.substr( 1, oLabel.size() - 2 );
		}
		m_oReferencedLabels.push_back( oLabel );
	}
}

void C6502Translator::AddDefinedLabel( string oLabel )
{
	if ( !oLabel.empty() )
	{
		if ( oLabel.substr(0,1) == "(" && oLabel.substr(oLabel.size() - 1, 1) == ")" )
		{
			oLabel = oLabel.substr( 1, oLabel.size() - 2 );
		}
		if ( oLabel.find( ':') != -1 )
		{
			oLabel = oLabel.substr(0, oLabel.find( ':') );
		}
		m_oDefinedLabels.push_back( oLabel );
	}
}

int CompareStrings( const string &pElem1, const string &pElem2 )
{
	return pElem1.compare(pElem2);
}

string C6502Translator::AddReferencedNotDefinedLabels()
{
	string oLabel;
	string oCode;
	string oStr;

	// sort arrays
	sort(m_oDefinedLabels.begin(), m_oDefinedLabels.end(), greater<string>());
	sort(m_oReferencedLabels.begin(), m_oReferencedLabels.end(), greater<string>());

	// remove duplicates from defined labels
	auto last_def = unique(m_oDefinedLabels.begin(), m_oDefinedLabels.end());
	m_oDefinedLabels.erase(last_def, m_oDefinedLabels.end());

	// remove duplicates from referenced labels
	auto last_ref = unique(m_oReferencedLabels.begin(), m_oReferencedLabels.end());
	m_oReferencedLabels.erase(last_ref, m_oReferencedLabels.end());

	for ( size_t iPos = 0; iPos < m_oReferencedLabels.size(); iPos++ )
	{
		oLabel = m_oReferencedLabels[iPos];
		if ( find(m_oDefinedLabels.begin(), m_oDefinedLabels.end(), oLabel) != m_oDefinedLabels.end())
		{
			char* pEnd = NULL;
			oStr = oLabel.substr(1);
			//oStr = oLabel;
			if ( strtol( oStr.c_str(), &pEnd, 16 ) != 0 && !*pEnd )
			{
				oCode += oLabel + " equ $" + oStr + "\n";
			}
			else
			{
				oCode += oLabel + " equ " + oStr + "\n";
			}
		}
	}
	return oCode;
}

void C6502Translator::Replace(string& str, const string& from, const string& to) {
	if (from.empty())
		return;
	for (size_t pos = 0; ; pos += to.length()) {
		// Locate the substring to replace
		pos = str.find(from, pos);
		if (pos == string::npos) break;
		// Replace by erasing and inserting
		str.erase(pos, from.length());
		str.insert(pos, to);
	}
}

bool C6502Translator::GetNextLine( string& oLine )
{
	int iPos = m_oZ80Code.find( "\n" );
	if ( iPos >= 0 )
	{
		oLine = m_oZ80Code.substr(0, iPos );
		m_oZ80Code = m_oZ80Code.substr( iPos + 1 );
	}
	else
	{
		oLine = m_oZ80Code;
		m_oZ80Code.clear();
	}
	return !oLine.empty() || !m_oZ80Code.empty();
}

int C6502Translator::CountParameters(string pattern)
{
	return count(pattern.begin(), pattern.end(), '%' );
}

string C6502Translator::string_format(const string fmt, ...) {
	int size = ((int)fmt.size()) * 2 + 50;   // Use a rubric appropriate for your code
	string str;
	va_list ap;
	while (1) {     // Maximum two passes on a POSIX system...
		str.resize(size);
		va_start(ap, fmt);
		int n = vsnprintf((char *)str.data(), size, fmt.c_str(), ap);
		va_end(ap);
		if (n > -1 && n < size) {  // Everything worked
			str.resize(n);
			return str;
		}
		if (n > -1)  // Needed size returned
			size = n + 1;   // For null char
		else
			size *= 2;      // Guess at a larger size (OS specific)
	}
	return str;
}

bool C6502Translator::ParsingOK( const string& oSource, const char *szPattern, const char* szArgument1, const char* szArgument2 )
{
	string oResult = string_format( szPattern, szArgument1, szArgument2 );
	return oSource == oResult;
}

bool C6502Translator::ParsingOK( const string& oSource, const char *szPattern, const char* szArgument1 )
{
	string oResult = string_format( szPattern, szArgument1 );
	return oSource == oResult;
}

bool C6502Translator::FixArg( char* szArgument )
{
	char *p=strchr(szArgument,')');
	if (!p) p=strchr(szArgument,',');
	if (p) *p = '\0';
	return true;
}

void C6502Translator::MakeParameterALabel( char *szArgument )
{
	string oParameter = string(szArgument);
	char *szEnd = NULL;
	if ( !oParameter.empty()
		&& (oParameter[0] != 'L')
		&& (oParameter[0] != '(')
		&& strtol(szArgument, &szEnd, 16) != 0L
		&& *szEnd == 0)
	{
		oParameter.insert(0, "L");
	}
	else
	{
		//oParameter = '$' + oParameter;
	}
	strncpy( szArgument, oParameter.c_str(), oParameter.size());
}

#define FLAG_MAKE_1ST_PARAM_A_LABEL		1

struct
{
	const char *szPattern;
	const char *sz6502Code;
	int iRead;
	int iWrite;
	int iFlag;
} g_oCodePatterns[] =
{
	{ "LD C,D", "lda z80_d|sta z80_c", D, C },
	{ "LD C,L", "lda z80_l|sta z80_c", L, C },
	{ "LD L,C", "lda z80_c|sta z80_l", C, L },
	{ "LD A,A", "ora #$00 ; WHY?", A, A },
	{ "LD H,A", "sta z80_h", A, H },
	{ "LD A,H", "lda z80_h", H, A },
	{ "LD C,A", "sta z80_c", A, C },
	{ "LD A,C", "lda z80_c", C, A },
	{ "LD L,A", "sta z80_l", A, L },
	{ "LD A,L", "lda z80_l", L, A },
	{ "LD E,A", "sta z80_e", A, E },
	{ "LD A,B", "lda z80_b", B, A },
	{ "LD B,A", "sta z80_b", A, B },
	{ "LD A,D", "lda z80_d", D, A },
	{ "LD D,A", "sta z80_d", A, D },
	{ "LD D,B", "lda z80_b|sta z80_d", B, D },
	{ "LD H,B", "lda z80_b|sta z80_h", B, H },
	{ "LD H,D", "lda z80_d|sta z80_h", D, H },
	{ "LD H,E", "lda z80_e|sta z80_h", E, H },
	{ "LD H,H", "lda z80_h|sta z80_h", H, H },
	{ "LD H,L", "lda z80_l|sta z80_h", L, H },
	{ "LD L,D", "lda z80_d|sta z80_l", D, L },
	{ "LD L,E", "lda z80_e|sta z80_l", E, L },
	{ "LD A,E", "lda z80_e", E, A },
	{ "LD B,C", "lda z80_c|sta z80_b", C, B },
	{ "LD B,H", "lda z80_h|sta z80_b", H, B },
	{ "LD E,L", "lda z80_l|sta z80_e", L, E },
	{ "LD E,C", "lda z80_c|sta z80_e", C, E },
	{ "LD H,C", "lda z80_c|sta z80_h", C, H },
	{ "LD D,L", "lda z80_l|sta z80_d", L, D },
	{ "LD D,H", "lda z80_h|sta z80_d", H, D },
	{ "LD A,R", "ld_a_r", R, A },
	{ "LD SP,HL", "; TODO: LD SP,HL", HL, SP },
	{ "LD  SP,HL", "; TODO: LD SP,HL", HL, SP},
	{ "LD A,$%02s", "lda #$%1", 0, A },
	{ "LD B,$%02s", "lda #$%1|sta z80_b", 0, B },
	{ "LD C,$%02s", "lda #$%1|sta z80_c", 0, C },
	{ "LD D,$%02s", "lda #$%1|sta z80_d", 0, D },
	{ "LD E,$%02s", "lda #$%1|sta z80_e", 0, E },
	{ "LD L,$%02s", "lda #$%1|sta z80_l", 0, L },
	{ "LD H,$%02s", "lda #$%1|sta z80_h", 0, H },
	{ "LD IY,%s", "lda #<%1|sta z80_iy|lda #>%1|sta z80_iy+1", 0, IY, FLAG_MAKE_1ST_PARAM_A_LABEL },
	{ "LD IX,%s", "lda #<%1|sta z80_ix|lda #>%1|sta z80_ix+1", 0, IX, FLAG_MAKE_1ST_PARAM_A_LABEL },
	{ "LD BC,(%05s)", "lda %1|sta z80_c|lda %1+1|sta z80_b", 0, HL, FLAG_MAKE_1ST_PARAM_A_LABEL },
	{ "LD BC,%s", "lda #<%1|sta z80_c|lda #>%1|sta z80_b", 0, BC, FLAG_MAKE_1ST_PARAM_A_LABEL },
	{ "LD DE,(%05s)", "lda %1|sta z80_e|lda %1+1|sta z80_d", 0, HL, FLAG_MAKE_1ST_PARAM_A_LABEL },
	{ "LD DE,%s", "lda #<%1|sta z80_e|lda #>%1|sta z80_d", 0, DE, FLAG_MAKE_1ST_PARAM_A_LABEL },
	{ "LD HL,(%05s)", "lda %1|sta z80_l|lda %1+1|sta z80_h", 0, HL, FLAG_MAKE_1ST_PARAM_A_LABEL },
	{ "LD HL,%s", "lda #<%1|sta z80_l|lda #>%1|sta z80_h", 0, HL, FLAG_MAKE_1ST_PARAM_A_LABEL },
	{ "LD A,(IX+%02s)", "ldy #$%1|lda (z80_ix),y", IX, A },
	{ "LD A,(IY+%02s)", "ldy #$%1|lda (z80_iy),y", IY, A },
	{ "LD B,(IX+%02s)", "ldy #$%1|lda (z80_ix),y|sta z80_b", IX, B },
	{ "LD C,(IX+%02s)", "ldy #$%1|lda (z80_ix),y|sta z80_c", IX, C },
	{ "LD D,(IX+%02s)", "ldy #$%1|lda (z80_ix),y|sta z80_d", IX, D},
	{ "LD L,(IX+%02s)", "ldy #$%1|lda (z80_ix),y|sta z80_l", IX, L },
	{ "LD H,(IX+%02s)", "ldy #$%1|lda (z80_ix),y|sta z80_h", IX, H },
	{ "LD (IX+%02s),A", "ldy #$%1|sta (z80_ix),y", A|IX, 0 },
	{ "LD (IX+%02s),B", "ldy #$%1|lda z80_b|sta (z80_ix),y", B|IX, 0 },
	{ "LD (IX+%02s),C", "ldy #$%1|lda z80_c|sta (z80_ix),y", C|IX, 0 },
	{ "LD (IX+%02s),L", "ldy #$%1|lda z80_l|sta (z80_ix),y", L|IX, 0 },
	{ "LD (IX+%02s),H", "ldy #$%1|lda z80_h|sta (z80_ix),y", H|IX, 0 },
	{ "LD (IX+%02s),%02s", "ldy #$%1|lda #$%2|sta (z80_ix),y", IX, 0 },
	{ "LD (IY+%02s),A", "ldy #$%1|sta (z80_iy),y", A|IY, 0 },
	{ "LD (IY+%02s),C", "ldy #$%1|lda z80_c|sta (z80_iy),y", C|IY, 0 },
	{ "LD (IY+%02s),D", "ldy #$%1|lda z80_d|sta (z80_iy),y", D|IY, 0 },
	{ "LD (IY+%02s),E", "ldy #$%1|lda z80_e|sta (z80_iy),y", E|IY, 0 },
	{ "LD (IY+%02s),%02s", "ldy #$%1|lda #$%2|sta (z80_iy),y", IY, 0 },
	{ "LD D,(IY+%02s)", "ldy #$%1|lda (z80_iy),y|sta z80_d", IY, D },
	{ "LD E,(IY+%02s)", "ldy #$%1|lda (z80_iy),y|sta z80_e", IY, E },
	{ "LD L,(IY+%02s)", "ldy #$%1|lda (z80_iy),y|sta z80_l", IY, L },
	{ "LD A,(HL)", "ldy #$00|lda (z80_hl),y", HL, A },
	{ "LD A,(DE)", "ldy #$00|lda (z80_de),y", DE, A },
	{ "LD A,(BC)", "ldy #$00|lda (z80_bc),y", BC, A },
	{ "LD (BC),A", "ldy #$00|sta (z80_bc),y", A|BC, 0 },
	{ "LD (DE),A", "ldy #$00|sta (z80_de),y", A|DE, 0 },
	{ "LD A,(%05s)", "lda %1", 0, A, FLAG_MAKE_1ST_PARAM_A_LABEL },
	{ "LD (%05s),A", "sta %1", A, 0, FLAG_MAKE_1ST_PARAM_A_LABEL },
	{ "LD B,(HL)", "ldy #$00|lda (z80_hl),y|sta z80_b", HL, B },
	{ "LD C,(HL)", "ldy #$00|lda (z80_hl),y|sta z80_c", HL, C },
	{ "LD D,(HL)", "ldy #$00|lda (z80_hl),y|sta z80_d", HL, D },
	{ "LD E,(HL)", "ldy #$00|lda (z80_hl),y|sta z80_e", HL, E },
	{ "LD H,(HL)", "ldy #$00|lda (z80_hl),y|sta z80_h", HL, H },
	{ "LD L,(HL)", "ldy #$00|lda (z80_hl),y|sta z80_l", HL, L },
	{ "LD (HL),A", "ldy #$00|sta (z80_hl),y", A|HL, 0 },
	{ "LD (HL),C", "lda z80_c|ldy #$00|sta (z80_hl),y", C|HL, 0 },
	{ "LD (HL),D", "lda z80_d|ldy #$00|sta (z80_hl),y", D|HL, 0 },
	{ "LD (HL),E", "lda z80_e|ldy #$00|sta (z80_hl),y", E|HL, 0 },
	{ "LD (HL),$%02s", "lda #$%1|ldy #$00|sta (z80_hl),y", HL, 0 },
	{ "LD SP,(%05s)", "; LD SP,(%1): TODO ", 0, SP},
	{ "LD SP,%s", "; LD SP,%1: TODO ", 0, SP},
	{ "LD (%05s),HL", "lda z80_l|sta %1|lda z80_h|sta %1+1", HL, 0, FLAG_MAKE_1ST_PARAM_A_LABEL },
	{ "LD A,(%s)", "lda %1", 0, A, FLAG_MAKE_1ST_PARAM_A_LABEL },
	{ "LD (%s),A", "sta %1", A, 0, FLAG_MAKE_1ST_PARAM_A_LABEL },
	{ "LD (%05s),SP", "; TODO: LD (%1),SP", SP, 0, FLAG_MAKE_1ST_PARAM_A_LABEL },
	{ "LD (%05s),HL", "lda z80_l|sta %1|lda z80_h|sta %1+1", HL, 0, FLAG_MAKE_1ST_PARAM_A_LABEL },
	{ "LD (%05s),DE", "lda z80_e|sta %1|lda z80_d|sta %1+1", DE, 0, FLAG_MAKE_1ST_PARAM_A_LABEL },

	{ "LDH   A,($%02s) ; %s", "lda $FF%1", 0, A },
	{ "LDH   ($%02s),A   ; %s", "sta $FF%1", A, 0 },

	{ "OR   (IY+%02s)", "ldy #$%1|ora (z80_iy),y", A|IY, A|FLAGS },
	{ "OR   (IX+%02s)", "ldy #$%1|ora (z80_ix),y", A|IX, A|FLAGS },
	{ "OR   A", "ora #$00 ; OR   A: Warning: probably tests for zero", A, A | FLAGS },
	{ "OR   B", "ora z80_b", A|B, A|FLAGS },
	{ "OR   C", "ora z80_c", A|C, A|FLAGS },
	{ "OR   D", "ora z80_d", A|D, A|FLAGS },
	{ "OR   E", "ora z80_e", A|E, A|FLAGS },
	{ "OR   L", "ora z80_l", A|L, A|FLAGS },
	{ "OR   H", "ora z80_h", A|H, A|FLAGS },
	{ "OR   (HL)", "ldy #$00|ora (z80_hl),y", A|HL, A|FLAGS },
	{ "OR   $%02s", "ora #$%1", A, A|FLAGS },

	{ "ADD A,A", "asl @", A, A|FLAGS },
	{ "ADD A,B", "clc|adc z80_b", A|B, A|FLAGS },
	{ "ADD A,C", "clc|adc z80_c", A|C, A|FLAGS },
	{ "ADD A,D", "clc|adc z80_d", A|D, A|FLAGS },
	{ "ADD A,E", "clc|adc z80_e", A|E, A|FLAGS },
	{ "ADD A,L", "clc|adc z80_l", A|L, A|FLAGS },
	{ "ADD A,H", "clc|adc z80_h", A|H, A|FLAGS },
	{ "ADD IY,DE", "jsr add_iy_de", IY|DE, IY|HFLAG|CARRY|NFLAG },
	{ "ADD IY,BC", "jsr add_iy_bc", IY|BC, IY|HFLAG|CARRY|NFLAG  },
	{ "ADD IX,DE", "jsr add_ix_de", IX|DE, IX|HFLAG|CARRY|NFLAG  },
	{ "ADD HL,DE", "jsr add_hl_de", HL|DE, HL|HFLAG|CARRY|NFLAG  },
	{ "ADD HL,BC", "jsr add_hl_bc", HL|BC, HL|HFLAG|CARRY|NFLAG  },
	{ "ADD HL,SP", "jsr add_hl_sp", HL|SP, HL|HFLAG|CARRY|NFLAG },
	{ "ADD HL,HL", "asl z80_l|rol z80_h", HL, HL|HFLAG|CARRY|NFLAG  },
	{ "ADD A,(HL)", "ldy #$00|clc|adc (z80_hl),y", A|HL, A|FLAGS },
	{ "ADD A,(IX+%02s)", "ldy #$%1|clc|adc (z80_ix),y", A|IX, A|FLAGS },
	{ "ADD A,(IY+%02s)", "ldy #$%1|clc|adc (z80_iy),y", A|IY, A|FLAGS },
	{ "ADD A,$%02s", "clc|adc #$%1", A, A|FLAGS },
	{ "ADD A", "asl @", A, A|FLAGS },

	{ "ADC   A,B", "adc z80_b", A|B|CARRY, A|FLAGS },
	{ "ADC   A,$%02s", "adc #$%1", A|CARRY, A|FLAGS },

	{ "SUB  (IX+%02s)", "ldy #$%1|sec|sbc (z80_ix),y", A|IX, A|FLAGS },
	{ "SUB  (IY+%02s)", "ldy #$%1|sec|sbc (z80_iy),y", A|IY, A|FLAGS },
	{ "SUB   B", "sec|sbc z80_b", A|B, A|FLAGS },
	{ "SUB   C", "sec|sbc z80_c", A|C, A|FLAGS },
	{ "SUB   D", "sec|sbc z80_d", A|D, A|FLAGS },
	{ "SUB   E", "sec|sbc z80_e", A|E, A|FLAGS },
	{ "SUB   L", "sec|sbc z80_l", A|L, A|FLAGS },
	{ "SUB   H", "sec|sbc z80_h", A|H, A|FLAGS },
	{ "SUB   (HL)", "ldy #$00|sec|sbc (z80_hl),y", A | HL, A | FLAGS },
	{ "SUB   $%02s", "sec|sbc #$%1", A, A|FLAGS },

	{ "SBC   A,A", "lda #$00|sec", A | A | FLAGS },
	{ "SBC   A,B", "sec|sbc z80_b", A | B, A | FLAGS },
	{ "SBC   A,H", "sec|sbc z80_h", A | H, A | FLAGS },
	{ "SBC   A,(HL)", "ldy #$00|sec|sbc (z80_hl),y", A | HL, A | FLAGS },
	{ "SBC   HL,DE", "jsr sbc_hl_de", HL|DE|CARRY, HL|FLAGS },
	{ "SBC   HL,BC", "jsr sbc_hl_bc", HL|BC|CARRY, HL|FLAGS },

	{ "AND   A", "; AND  A: Warning: probably tests for zero", A, A|FLAGS },
	{ "AND   B", "and z80_b", A|B, FLAGS },
	{ "AND   C", "and z80_c", A|C, FLAGS },
	{ "AND   D", "and z80_d", A|D, FLAGS },
	{ "AND   L", "and z80_l", A|L, FLAGS },
	{ "AND   (HL)", "ldy #$00|and (z80_hl),y", A|HL, A|FLAGS },
	{ "AND   $%02s", "and #$%1", A, A|FLAGS },

	{ "CP   B", "cmp z80_b", A|B, FLAGS },
	{ "CP   C", "cmp z80_c", A|C, FLAGS },
	{ "CP   D", "cmp z80_d", A|D, FLAGS },
	{ "CP   E", "cmp z80_e", A|E, FLAGS },
	{ "CP   H", "cmp z80_h", A|H, FLAGS },
	{ "CP   L", "cmp z80_l", A|L, FLAGS },
	{ "CP   (HL)", "ldy #$00|cmp (z80_hl),y", A|HL, FLAGS },
	{ "CP   (IX+%02s)", "ldy #$%1|cmp (z80_ix),y", A|IX, FLAGS },
	{ "CP   (IY+%02s)", "ldy #$%1|cmp (z80_iy),y", A|IY, FLAGS },
	{ "CP   $%02s", "cmp #$%1", A, FLAGS },

	{ "NEG", "eor #$ff|clc|adc #$01", A, A|FLAGS },

	{ "CPL", "eor #$ff", A, A|NFLAG|HFLAG },

	{ "XOR   A", "lda #$00", 0, A|FLAGS },
	{ "XOR   B", "eor z80_b", A|B, FLAGS },
	{ "XOR   C", "eor z80_c", A|C, FLAGS },
	{ "XOR   D", "eor z80_d", A|D, FLAGS },
	{ "XOR   E", "eor z80_e", A|E, FLAGS },
	{ "XOR   (HL)", "ldy #$00|eor (z80_hl),y", A|HL, A|FLAGS },
	{ "XOR   (IX+%02s)", "ldy #$%1|eor (z80_ix),y", A|IX, A|FLAGS },
	{ "XOR   $%02s", "eor #$%1", A, A|FLAGS },

	{ "INC A", "clc|adc #$01", A, A|SIGN|ZERO|HFLAG|OVERFLOWFLAG|NFLAG },
	{ "INC B", "inc z80_b", B, B|SIGN|ZERO|HFLAG|OVERFLOWFLAG|NFLAG },
	{ "INC C", "inc z80_c", C, C|SIGN|ZERO|HFLAG|OVERFLOWFLAG|NFLAG },
	{ "INC D", "inc z80_d", D, D|SIGN|ZERO|HFLAG|OVERFLOWFLAG|NFLAG },
	{ "INC E", "inc z80_e", E, E|SIGN|ZERO|HFLAG|OVERFLOWFLAG|NFLAG },
	{ "INC L", "inc z80_l", L, L|SIGN|ZERO|HFLAG|OVERFLOWFLAG|NFLAG },
	{ "INC H", "inc z80_h", H, H|SIGN|ZERO|HFLAG|OVERFLOWFLAG|NFLAG },
	{ "INC HL", "inc z80_l|bne *+4|inc z80_h", HL, HL },
	{ "INC BC", "inc z80_c|bne *+4|inc z80_b", BC, BC },
	{ "INC DE", "inc z80_e|bne *+4|inc z80_d", DE, DE },
	{ "INC IX", "inc z80_ix|bne *+4|inc z80_ix+1", IX, IX },
	{ "INC IY", "inc z80_iy|bne *+4|inc z80_iy+1", IY, IY },
	{ "INC SP", "pla", SP, SP },
	{ "INC (IX+%02s)", "ldy #$%1|lda (z80_ix),y|clc|adc #$01|sta (z80_ix),y", IX, SIGN|ZERO|HFLAG|OVERFLOWFLAG|NFLAG },
	{ "INC (HL)", "ldy #$00|lda (z80_hl),y|clc|adc #$01|sta (z80_hl),y", HL, SIGN|ZERO|HFLAG|OVERFLOWFLAG|NFLAG },

	{ "DEC   A", "sec|sbc #$01", A, A|SIGN|ZERO|HFLAG|OVERFLOWFLAG|NFLAG },
	{ "DEC   B", "dec z80_b", B, B|SIGN|ZERO|HFLAG|OVERFLOWFLAG|NFLAG },
	{ "DEC   C", "dec z80_c", C, C|SIGN|ZERO|HFLAG|OVERFLOWFLAG|NFLAG },
	{ "DEC   D", "dec z80_d", D, D|SIGN|ZERO|HFLAG|OVERFLOWFLAG|NFLAG },
	{ "DEC   E", "dec z80_e", E, E|SIGN|ZERO|HFLAG|OVERFLOWFLAG|NFLAG },
	{ "DEC   H", "dec z80_h", H, H|SIGN|ZERO|HFLAG|OVERFLOWFLAG|NFLAG },
	{ "DEC   L", "dec z80_l", L, L|SIGN|ZERO|HFLAG|OVERFLOWFLAG|NFLAG },
	{ "DEC   (HL)", "ldy #$00|lda (z80_hl),y|sec|sbc #$01|sta (z80_hl),y", HL, SIGN|ZERO|HFLAG|OVERFLOWFLAG|NFLAG },
	{ "DEC   (IX+%02s)", "ldy #$%1|lda (z80_ix),y|sec|sbc #$01|sta (z80_ix),y", IX, SIGN|ZERO|HFLAG|OVERFLOWFLAG|NFLAG },
	{ "DEC   IX", "jsr dec_ix", IX, IX },
	{ "DEC   HL", "jsr dec_hl", HL, HL },
	{ "DEC   BC", "jsr dec_bc", BC, BC },
	{ "DEC   DE", "jsr dec_de", DE, DE },
	{ "DEC   SP", "pha", SP, SP },

	{ "EX    DE,HL", "jsr ex_de_hl", DE|HL, DE|HL },

	{ "RRCA", "lsr @ ; RRCA: Warning: this copies bit 0 to bit 7", A, A|HFLAG|NFLAG|CARRY },
	{ "RRC   A", "lsr @ ; RRCA: Warning: this copies bit 0 to bit 7", A, A|HFLAG|NFLAG|CARRY },
	{ "RLCA", "asl @ ; RLCA: Warning: this copies bit 7 to bit 0", A, A|HFLAG|NFLAG|CARRY },
	{ "RRA", "ror @", A|CARRY, A|HFLAG|NFLAG|CARRY },
	{ "SRA   A", "cmp #$80|ror @", A, A|FLAGS },
	{ "SRL   A", "lsr @", A, A|FLAGS },
	{ "SRL   E", "lsr z80_e", E, E|FLAGS },
	{ "SRL   D", "lsr z80_d", D, D|FLAGS },
	{ "SRL   B", "lsr z80_b", B, B|FLAGS },
	{ "SRL   C", "lsr z80_c", C, C|FLAGS },
	{ "SLA   A", "asl @", A, A|FLAGS },
	{ "SLA   C", "asl z80_c", C, C|FLAGS },
	{ "SLA   E", "asl z80_e", E, E|FLAGS },
	{ "SRL   H", "lsr z80_h", H, H|FLAGS },
	{ "RL   C", "rol z80_c", C|CARRY, C|FLAGS },
	{ "RL   D", "rol z80_d", D|CARRY, D|FLAGS },
	{ "RL   E", "rol z80_e", E|CARRY, E|FLAGS },
	{ "RR   C", "ror z80_c", C|CARRY, C|FLAGS },
	{ "RR   L", "ror z80_l", L|CARRY, L|FLAGS },

	{ "CALL   C,%s", "bcs *+5|jsr %1 ; CALL C: Warning: check carry", CARRY, 0 },
	{ "CALL   NC,%s", "bcc *+5|jsr %1 ; CALL NC: Warning: check carry", CARRY, 0 },
	{ "CALL   Z,%s", "bne *+5|jsr %1", ZERO, 0 },
	{ "CALL   NZ,%s", "beq *+5|jsr %1", ZERO, 0 },
	{ "CALL   %s", "jsr %1", 0, 0 },

	{ "JP   Z,%s", "jeq %1", ZERO, 0 },
	{ "JP   NZ,%s", "jne %1", ZERO, 0 },
	{ "JP   NC,%s", "jcs %1", CARRY, 0 },
	{ "JP   C,%s", "jcc %1", CARRY, 0 },
	{ "JP   M,%s", "jmi %1", SIGN, 0 },
	{ "JP   P,%s", "jpl %1", SIGN, 0 },
	{ "JP   (HL)", "jmp (z80_hl)", HL, 0 },
	{ "JP   %s", "jmp %1", 0, 0 },

	{ "DJNZ %s", "dec z80_b|jne %1", B, B },
	{ "JR   Z,%s", "jeq %1", ZERO, 0 },
	{ "JR   C,%s", "jcc %1", CARRY, 0 },
	{ "JR   NC,%s", "jcs %1", CARRY, 0 },
	{ "JR   NZ,%s", "jne %1", ZERO, 0 },
	{ "JR   %s", "jmp %1", 0, 0 },

	{ "BIT  %1s,(IX+%02s)", "ldy #$%2|lda (z80_ix),y|bit _bitmem%1", IX, ZERO|HFLAG|NFLAG },
	{ "SET  %1s,(IX+%02s)", "ldy #$%2|lda (z80_ix),y|ora #_bitvalue%1|sta (z80_ix),y", IX, 0 },
	{ "RES  %1s,(IX+%02s)", "ldy #$%2|lda (z80_ix),y|and #_notbitvalue%1|sta (z80_ix),y", IX, 0 },
	{ "BIT  %1s,(IY+%02s)", "ldy #$%2|lda (z80_iy),y|bit _bitmem%1", IY, ZERO|HFLAG|NFLAG },
	{ "SET  %1s,(IY+%02s)", "ldy #$%2|lda (z80_iy),y|ora #_bitvalue%1|sta (z80_iy),y", IY, 0 },
	{ "RES  %1s,(IY+%02s)", "ldy #$%2|lda (z80_iy),y|and #_notbitvalue%1|sta (z80_iy),y", IY, 0 },
	{ "RES  %1s,(HL)", "ldy #$00|lda (z80_hl),y|and #_notbitvalue%1|sta (z80_hl),y", HL, 0 },
	{ "SET  %1s,(HL)", "ldy #$00|lda (z80_hl),y|ora #_bitvalue%1|sta (z80_hl),y", HL, 0 },
	{ "BIT  %1s,(HL)", "ldy #$00|lda (z80_hl),y|bit _bitmem%1", HL, ZERO|HFLAG|NFLAG },
	{ "BIT  %1s,E", "lda z80_e|bit _bitmem%1", E, ZERO|HFLAG|NFLAG  },
	{ "BIT  %1s,C", "lda z80_c|bit _bitmem%1", C, ZERO|HFLAG|NFLAG  },
	{ "SET  %1s,C", "lda z80_c|ora #_bitvalue%1|sta z80_c", C, C },
	{ "RES  %1s,C", "lda z80_c|and #_notbitvalue%1|sta z80_c", C, C },
	{ "BIT  %1s,A", "bit _bitmem%1", A, ZERO|HFLAG|NFLAG  },
	{ "SET  %1s,A", "lda z80_a|ora #_bitvalue%1|sta z80_a", A, A }, // added
	{ "RES  %1s,A", "lda z80_a|and #_notbitvalue%1|sta z80_a", A, A }, // added
	{ "SET  %s,A", "lda z80_a|ora #%1|sta z80_a", A, A }, // added
	{ "RES  %s,A", "lda z80_a|and #%1|sta z80_a", A, A }, // added

	{ "RETI", "rti", 0, 0 },
	{ "RET", "rts", 0, 0 },
	{ "RET   NZ", "beq *+3|rts", ZERO, 0 },
	{ "RET   NC", "bcc *+3|rts", CARRY, 0 },
	{ "RET   C", "bcs *+3|rts", CARRY, 0 },
	{ "RET   Z", "bne *+3|rts", ZERO, 0 },
	{ "RET   M", "bpl *+3|rts", SIGN, 0 },
	{ "RET   P", "bmi *+3|rts", SIGN, 0 },

	{ "PUSH   DE", "lda z80_e|pha|lda z80_d|pha", DE, SP },
	{ "PUSH   HL", "lda z80_l|pha|lda z80_h|pha", HL, SP },
	{ "POP   HL", "pla|sta z80_h|pla|sta z80_l", SP, HL },
	{ "POP   DE", "pla|sta z80_d|pla|sta z80_e", SP, DE },
	{ "PUSH   IX", "lda z80_ix|pha|lda z80_ix+1|pha", IX, SP },
	{ "POP   IX", "pla|sta z80_ix+1|pla|sta z80_ix", SP, IX },
	{ "PUSH   IY", "lda z80_iy|pha|lda z80_iy+1|pha", IY, SP },
	{ "POP   IY", "pla|sta z80_iy+1|pla|sta z80_iy", SP, IY },
	{ "PUSH   BC", "lda z80_c|pha|lda z80_b|pha", BC, SP },
	{ "POP   BC", "pla|sta z80_b|pla|sta z80_c", SP, BC },
	{ "PUSH   AF", "pha|php", A|FLAGS, SP },
	{ "POP   AF", "plp|pha", SP, A|FLAGS },

	{ "LDIR", "jsr ldir", HL|DE|BC, HL|DE|BC|HFLAG|OVERFLOWFLAG|NFLAG },
	{ "LDDR", "jsr lddr", HL|DE|BC, HL|DE|BC|HFLAG|OVERFLOWFLAG|NFLAG },

	{ "EX   AF,AF'", "ldx z80_ap|sta z80_ap|txa ; TODO: EX   AF,AF'", A|FLAGS, A|FLAGS },

	{ "EXX", "jsr exx ; TODO: EXX", HL|DE|BC, HL|DE|BC },

	{ "EX   (SP),HL", "jsr ex_sp_hl; TODO: EX   (SP),HL", SP|HL, HL },

	{ "SCF", "sec", 0, CARRY },
	{ "NOP", "nop", 0, 0 },

	{ "DAA", "; TODO: decimal mode", A|CARRY|HFLAG, A|CARRY|HFLAG|SIGN|ZERO|OVERFLOWFLAG },

	{ "OUT  (FE),A", "jsr out_fe", A, 0 },
	{ "OUT  (FD),A", "jsr out_fd", A, 0 },

	{ "IN   A,(FE)", "jsr in_a_fe", 0, A },
	{ "IN   A,(1F)", "jsr in_a_1f", 0, A },

	{ "EI", "jsr ei", 0, 0 },
	{ "DI", "jsr di", 0, 0 },

	{ "DW ", "; .wo 0", 0, 0 },
	{ "DW %s", ".wo %1", 0, 0 },
	{ "DB %s", ".he %1", 0, 0 },

	{ NULL, NULL, 0, 0 }
};

string C6502Translator::Translate()
{
	string oCodeLine;
	string oLabel;
	string oInstruction;
	bool bParsed = false;
	string oZ80Code;
	string o6502CodeLine;
	string o6502Code;
	string oAllCode;
	int iPos, iPadSpaces = 8;
	int iPreviousInstructionPattern = -1;
	int iPattern;
	regex e ("\\b([ ]+)([^ ]*)");
	string fmt ("   $2");

	char szArgument1[128], szArgument2[128];

	while ( GetNextLine( oCodeLine ) )
	{
		if ( oCodeLine.empty() )
		{
			// flush block
			oAllCode += oZ80Code + "\n" + o6502Code + "\n";
			oZ80Code.clear();
			o6502Code.clear();
			iPreviousInstructionPattern = -1;
			continue;
		}

		Replace(oCodeLine, "\t", string(iPadSpaces, ' '));

		oZ80Code += ";" + oCodeLine + "\n";

		if ( oCodeLine[0] == ';' )
		{
			// comment
			o6502Code += oCodeLine + "\n";
			continue;
		}

		if (oCodeLine.length() > 7 && oCodeLine.at(7) != ' ' )
		{
			// label definition without mnemonic
			o6502Code += oCodeLine + "\n";
			AddDefinedLabel( oCodeLine );
			continue;
		}

		oLabel = oCodeLine.substr(0, iPadSpaces);
		oLabel.erase(find_if(oLabel.rbegin(), oLabel.rend(),
			not1(ptr_fun<int, int>(isspace))).base(), oLabel.end()); // trim right

		if (oLabel.empty() || (oLabel.find(':') == string::npos))
		{
			oLabel.clear();
			iPos = iPadSpaces;
			o6502CodeLine = string(iPadSpaces, ' ');
		}
		else
		{
			o6502CodeLine = string_format("%-8s", oLabel.c_str());
			iPos = oLabel.size();
			oLabel.erase(remove(oLabel.begin(), oLabel.end(), ':'), oLabel.end()); // Remove
			AddDefinedLabel(oLabel);
		}

		oInstruction = oCodeLine.substr(iPos);

		char buf[128];
		char buf2[128];
		int blen = oInstruction.size() + 1;
		if (blen > 128) blen = 128;
		strncpy(&buf[0], oInstruction.c_str(), blen); buf[--blen]='\0';
		*regex_replace(&buf2[0], &buf[0], &buf[0]+strlen(buf), e, fmt) = '\0';
		oInstruction = string(buf2);
		::transform(oInstruction.begin(), oInstruction.end(), oInstruction.begin(), ::toupper);
		Replace(oInstruction, ", ", ",");
		Replace(oInstruction, "[", "(");
		Replace(oInstruction, "]", ")");
		bParsed = false;
		for ( iPattern = 0; !bParsed && g_oCodePatterns[iPattern].szPattern != NULL; iPattern++ )
		{
			string pattern = g_oCodePatterns[iPattern].szPattern;
			//pattern.Replace("%02s", " %s");
			Replace(pattern, "%05s", " %s");
			Replace(pattern, "%1s", " %s");
			Replace(pattern, ", %s", ",%s");
			Replace(pattern, "( %", "(%");
			Replace(pattern, "$ %", "$%");
			bool test=(iPattern==143 && oInstruction.substr(0,2) == "CP");
			switch( CountParameters( pattern ) )
			{
				case 0:
					if ( oInstruction == g_oCodePatterns[iPattern].szPattern )
					{
						o6502CodeLine += g_oCodePatterns[iPattern].sz6502Code;
						bParsed = true;
					}
					break;
				case 1:
					if ( sscanf( oInstruction.c_str(), pattern.c_str(), szArgument1 ) == 1 &&
						FixArg( szArgument1 ) && ParsingOK( oInstruction, pattern.c_str(), szArgument1 ) )
					{
						o6502CodeLine += g_oCodePatterns[iPattern].sz6502Code;
						if ( g_oCodePatterns[iPattern].iFlag == FLAG_MAKE_1ST_PARAM_A_LABEL )
						{
							MakeParameterALabel( szArgument1 );
							string oArgument1(szArgument1);
							AddReferencedLabel(oArgument1);
						}
						string oArgument1(szArgument1);
						if ( oInstruction.substr(0,2) == "DB" )
						{
							oArgument1.erase(remove(oArgument1.begin(), oArgument1.end(), 'h'), oArgument1.end()); // Remove
						}
						Replace(o6502CodeLine, "%1", oArgument1);
						bParsed = true;
					}
					break;
				case 2:
					if ( sscanf( oInstruction.c_str(), pattern.c_str(), szArgument1, szArgument2 ) == 2 &&
						FixArg( szArgument1 ) && ParsingOK( oInstruction, pattern.c_str(), szArgument1, szArgument2 ) )
					{
						o6502CodeLine += g_oCodePatterns[iPattern].sz6502Code;
						string oArgument1(szArgument1);
						Replace(o6502CodeLine, "%1", oArgument1);
						string oArgument2(szArgument2);
						Replace(o6502CodeLine, "%2", oArgument2);
						bParsed = true;
					}
					break;
			}
		}
		if ( bParsed )
		{
			iPos = o6502CodeLine.find( '|' );
			if ( iPos != -1 )
			{
				o6502CodeLine.insert( iPos, " ; " + oInstruction );
			}
			else
			{
				o6502CodeLine += " ; " + oInstruction;
			}
			Replace(o6502CodeLine, "|", "\n        ");
			iPattern -= 1;
			if ( (g_oCodePatterns[iPattern].iRead & A)
				&& iPreviousInstructionPattern != -1
				&& !(g_oCodePatterns[iPreviousInstructionPattern].iWrite & A)
				&& !(g_oCodePatterns[iPreviousInstructionPattern].iRead & A)
				//&& !(g_oCodePatterns[iPreviousInstructionPattern].iWrite & BC|DE|HL|IX|IY)
				)
			{
				o6502CodeLine += "; Warning: A read but not set by previous instruction";
			}
			if ( (g_oCodePatterns[iPattern].iRead & A)
				&& iPreviousInstructionPattern == -1
				)
			{
				o6502CodeLine += "; Warning: A read at the beginning of the block";
			}
			if ( (g_oCodePatterns[iPattern].iRead & A) && !oLabel.empty() )
			{
				o6502CodeLine += "; Warning: A read at jump target instruction";
			}
			if ( (g_oCodePatterns[iPattern].iRead & ZERO) && iPreviousInstructionPattern != -1 && !(g_oCodePatterns[iPreviousInstructionPattern].iWrite & ZERO))
			{
				o6502CodeLine += "; Warning: ZERO flag read but not set by previous instruction";
			}
			if ( (g_oCodePatterns[iPattern].iRead & SIGN) && iPreviousInstructionPattern != -1 && !(g_oCodePatterns[iPreviousInstructionPattern].iWrite & SIGN))
			{
				o6502CodeLine += "; Warning: SIGN flag read but not set by previous instruction";
			}
			if ( (g_oCodePatterns[iPattern].iRead & CARRY)
				&& iPreviousInstructionPattern != -1
				&& (strncmp( g_oCodePatterns[iPattern].szPattern, "JP", 2 ) == 0 || strncmp( g_oCodePatterns[iPattern].szPattern, "JR", 2 ) == 0 || strncmp( g_oCodePatterns[iPattern].szPattern, "RET", 3 ) == 0 || strncmp( g_oCodePatterns[iPattern].szPattern, "CALL", 4 ) == 0 ) )
			{
				if ( !(g_oCodePatterns[iPreviousInstructionPattern].iWrite & CARRY) )
				{
					o6502CodeLine += "; Warning: CARRY flag read but not set by previous instruction";
				}
				else if ( strncmp( g_oCodePatterns[iPreviousInstructionPattern].szPattern, "CP", 2 ) != 0
					&& strncmp( g_oCodePatterns[iPreviousInstructionPattern].szPattern, "SUB", 3 ) != 0
					&& strncmp( g_oCodePatterns[iPreviousInstructionPattern].szPattern, "SBC", 3 ) != 0
					)
				{
					o6502CodeLine += "; Warning: CARRY flag read but not set not by CP/SUB/SBC instruction, may need to invert";
				}
			}
			iPreviousInstructionPattern = iPattern;
		}
		else if (oInstruction.size() > 0)
		{
//			AfxMessageBox( "Syntax Error: " + oInstruction );
			o6502CodeLine = "; Syntax Error: " + oInstruction;
		}
		o6502Code += (o6502CodeLine + "\n");
	}

	oAllCode += oZ80Code + "\n" + o6502Code;

	oAllCode = AddReferencedNotDefinedLabels() + "\n\n" + oAllCode;

	return oAllCode;
}

int main(int argc, char* argv[])
{
	int nRetCode = 0;
	string oCode;
	//
	try
	{
		string fName = "D:\\Source\\Repos\\pokeyellow\\home.asm";
		if (argc == 2) fName = argv[1];
		ifstream oFile;
		oFile.open(fName, ifstream::in);
		char c = oFile.get();
		while (oFile.good()) {
			oCode.push_back(c);
			c = oFile.get();
		}
		oFile.close();
	}
	catch(ios_base::failure f)
	{
		fprintf(stderr, "%s", f.what());
	}
	//
	//oCode = "@@73    LD (IX+09),C\n";
	C6502Translator oTranslator( oCode );
	string o6502Code = oTranslator.Translate();
	fprintf(stdout, "%s", o6502Code.c_str());
	return nRetCode;
}
